/****************************************************************************\
*                                                                            *
*  Copyright 2008 The CAnt2 Project (http://code.google.com/p/cant2)         *
*                                                                            *
*  Licensed under the Apache License, Version 2.0 (the "License");           *
*  you may not use this file except in compliance with the License.          *
*  You may obtain a copy of the License at                                   *
*                                                                            *
*      http://www.apache.org/licenses/LICENSE-2.0                            *
*                                                                            *
*  Unless required by applicable law or agreed to in writing, software       *
*  distributed under the License is distributed on an "AS IS" BASIS,         *
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *
*  See the License for the specific language governing permissions and       *
*  limitations under the License.                                            *
*                                                                            *
\****************************************************************************/

#include "XmlCharacterClassification.h"
#include "FastBoolean.h"

#include <limits>

using namespace FastBoolean;

namespace Xml1p1 {

    const char cTrue  = 1;
    const char cFalse = 0;

    bool_t IsCharacter_0 ( const int ucs8 )
    {
        if ( ucs8 <  0x000001 ) return 0;
        if ( ucs8 <= 0x00d7ff ) return 1;
        if ( ucs8 <  0x00e000 ) return 0;
        if ( ucs8 <= 0x00fffd ) return 1;
        if ( ucs8 <  0x010000 ) return 0;
        if ( ucs8 <= 0x10ffff ) return 1;
        return 0;
    }

    bool_t IsCharacter_1 ( const int ucs8 )
    {
        bool_t r;
        r  = ( ( ucs8 >= 0x000001 ) ? cTrue : cFalse ) & ( ( ucs8 <= 0x00d7ff ) ? cTrue : cFalse );
        r |= ( ( ucs8 >= 0x00e000 ) ? cTrue : cFalse ) & ( ( ucs8 <= 0x00fffd ) ? cTrue : cFalse );
        r |= ( ( ucs8 >= 0x010000 ) ? cTrue : cFalse ) & ( ( ucs8 <= 0x10ffff ) ? cTrue : cFalse );
        return r;
    }

    bool_t IsCharacter_2 ( const int ucs8 )
    {
        bool_t r;
        r  = ( ( ucs8 >= 0x000001 ) ? cTrue : cFalse ) & ( ( ucs8 <= 0x00d7ff ) ? cTrue : cFalse );

        if ( r ) return r;

        r |= ( ( ucs8 >= 0x00e000 ) ? cTrue : cFalse ) & ( ( ucs8 <= 0x00fffd ) ? cTrue : cFalse );
        r |= ( ( ucs8 >= 0x010000 ) ? cTrue : cFalse ) & ( ( ucs8 <= 0x10ffff ) ? cTrue : cFalse );
        return r;
    }

    bool_t IsCharacter_3 ( const int ucs8 )
    {
        bool_t r;
        r  = Less ( 0x000000, ucs8 );
        r ^= Less ( 0x00d7ff, ucs8 );
        r ^= Less ( 0x00dfff, ucs8 );
        r ^= Less ( 0x00fffd, ucs8 );
        r ^= Less ( 0x00ffff, ucs8 );
        r ^= Less ( 0x10ffff, ucs8 );
        return r;
    }

    bool_t IsCharacter ( const int ucs8 )
    {
        return IsCharacter_3 ( ucs8 );
    }

    /*
    bool IsRestrictedCharacter ( int ucs8 )
    {
        if ( ( ucs8 >= 0x000020 ) && ( ucs8 <= 0x00007e ) ) return false;
        if ( ucs8 >= 0x0000a0 ) return false;

        if ( ( ucs8 >= 0x000001 ) && ( ucs8 <= 0x000008 ) ) return true;
        if ( ( ucs8 >= 0x00000b ) && ( ucs8 <= 0x00000c ) ) return true;
        if ( ( ucs8 >= 0x00000e ) && ( ucs8 <= 0x00001f ) ) return true;
        if ( ( ucs8 >= 0x00007f ) && ( ucs8 <= 0x000084 ) ) return true;
        if ( ( ucs8 >= 0x000086 ) && ( ucs8 <= 0x00009f ) ) return true;

        return false;
    }
    */

    bool_t IsWhitespace_0 ( const int ucs8 )
    {
        return  ( ucs8 == 0x20 )
            ||  ( ucs8 == 0x09 )
            ||  ( ucs8 == 0x0d )
            ||  ( ucs8 == 0x0a )
            ;
    }

    int Select_0 ( int a, int b, int c, int d )
    {
        return ( a < b ) ? c : d;
    }

    int Select_1 ( int a, int b, int c, int d )
    {
        int m = ( ( a < b ) ? 0 : 1 ) - 1;
        return ( ( c ^ d ) & m ) ^ d;
    }

    int Select_2 ( int a, int b, int c, int d )
    {
        return ( ( ( ( a - b ) >> 31 ) & ( c ^ d ) ) ^ d );
    }

    int Select ( int a, int b, int c, int d )
    {
        // TODO : Figure out which implementation is best. But may have no simple answer.
        return Select_1 ( a, b, c, d );
    }

    bool_t IsWhitespace_1 ( const int ucs8 )
    {
        const int min = 0x08;
        const int max = 0x21;

        int x = Select ( ucs8 , max,  ucs8 , max );
        x -= min;
        x = Select ( x , 0 , 0 , x );

        const unsigned bitArray =
                ( 1 << ( 0x20 - min ) )
            |   ( 1 << ( 0x09 - min ) )
            |   ( 1 << ( 0x0d - min ) )
            |   ( 1 << ( 0x0a - min ) )
            ;

        return ( bitArray & ( 1 << x ) ) != 0;
    }

    bool_t IsWhitespace_2 ( const int ucs8 )
    {
        bool_t r;
        r  = Equals ( ucs8, 0x20 );
        r |= Equals ( ucs8, 0x09 );
        r |= Equals ( ucs8, 0x0d );
        r |= Equals ( ucs8, 0x0a );
        return r;
    }

    bool_t IsWhitespace ( const int ucs8 )
    {
        return IsWhitespace_2 ( ucs8 );
    }

    bool_t IsNameStart ( const int ucs8 )
    {
        bool_t r;

        r = Equals ( ucs8, ':' );
        r |= InRangeInclusive ( 'A', 'Z' ) ( ucs8 );
        r |= Equals ( ucs8, '_' );
        r |= InRangeInclusive ( 'a', 'z' ) ( ucs8 );

        if ( ucs8 < 0x0000c0 ) return r;
        if ( ucs8 < 0x0000d7 ) return True;
        if ( ucs8 < 0x0000d8 ) return False;
        if ( ucs8 < 0x0000f7 ) return True;
        if ( ucs8 < 0x0000f8 ) return False;
        if ( ucs8 < 0x000300 ) return True;
        if ( ucs8 < 0x000370 ) return False;
        if ( ucs8 < 0x00037e ) return True;
        if ( ucs8 < 0x00037f ) return False;
        if ( ucs8 < 0x002000 ) return True;
        if ( ucs8 < 0x00200c ) return False;
        if ( ucs8 < 0x00200e ) return True;
        if ( ucs8 < 0x002070 ) return False;
        if ( ucs8 < 0x002190 ) return True;
        if ( ucs8 < 0x002c00 ) return False;
        if ( ucs8 < 0x002ff0 ) return True;
        if ( ucs8 < 0x003001 ) return False;
        if ( ucs8 < 0x00d800 ) return True;
        if ( ucs8 < 0x00f900 ) return False;
        if ( ucs8 < 0x00fdd0 ) return True;
        if ( ucs8 < 0x00fdf0 ) return False;
        if ( ucs8 < 0x00fffe ) return True;
        if ( ucs8 < 0x010000 ) return False;
        if ( ucs8 < 0x0f0000 ) return True;
        return False;
    }

    bool_t IsNameContinue ( const int ucs8 )
    {
        bool_t r;

        r  = Equals ( ucs8, '-' );
        r |= Equals ( ucs8, '.' );
        r |= InRangeInclusive ( '0', '9' ) ( ucs8 );
        r |= Equals ( ucs8, ':' );
        r |= InRangeInclusive ( 'A', 'Z' ) ( ucs8 );
        r |= Equals ( ucs8, '_' );
        r |= InRangeInclusive ( 'a', 'z' ) ( ucs8 );

        if ( ucs8 < 0x0000b7 ) return r;
        if ( ucs8 < 0x0000b8 ) return True;
        if ( ucs8 < 0x0000c0 ) return False;
        if ( ucs8 < 0x0000d7 ) return True;
        if ( ucs8 < 0x0000d8 ) return False;
        if ( ucs8 < 0x0000f7 ) return True;
        if ( ucs8 < 0x0000f8 ) return False;
        if ( ucs8 < 0x00037e ) return True;
        if ( ucs8 < 0x00037f ) return False;
        if ( ucs8 < 0x002000 ) return True;
        if ( ucs8 < 0x00200c ) return False;
        if ( ucs8 < 0x00200e ) return True;
        if ( ucs8 < 0x00203f ) return False;
        if ( ucs8 < 0x002041 ) return True;
        if ( ucs8 < 0x002070 ) return False;
        if ( ucs8 < 0x002190 ) return True;
        if ( ucs8 < 0x002c00 ) return False;
        if ( ucs8 < 0x002ff0 ) return True;
        if ( ucs8 < 0x003001 ) return False;
        if ( ucs8 < 0x00d800 ) return True;
        if ( ucs8 < 0x00f900 ) return False;
        if ( ucs8 < 0x00fdd0 ) return True;
        if ( ucs8 < 0x00fdf0 ) return False;
        if ( ucs8 < 0x00fffe ) return True;
        if ( ucs8 < 0x010000 ) return False;
        if ( ucs8 < 0x0f0000 ) return True;

        return False;
    }
}

